﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

#pragma once

#include "GmepDevService.h"
#include <cstdint>

#pragma warning( push )
#pragma warning( disable : 4251 )

//////////////////////////////////////////////////////////////////////////
// 使用方法
// 独立计时器
//     1. 创建计时器。
//       1.1. 创建单个：CREATE_TIMER(TIMER, OWNER)。如CREATE_TIMER(NewFileCmd, L"周智华")。
//       1.2. 一次创建多个独立计时器：CREATE_TIMER2，CREATE_TIMER3，CREATE_TIMER4。末尾数字表示一次创建计时器的个数。
//       1.3. 如果计时器需要在别的DLL的使用，则需使用相应的EXPORT版本创建计时器。即CREATE_TIMER_EXPORT，CREATE_TIMER_EXPORT2，CREATE_TIMER_EXPORT3，CREATE_TIMER_EXPORT4。如CREATE_TIMER_EXPORT(REGENERATION, L"周智华")。
//     2. 声明计时器。
//       2.1. 如果计时器使用和计时器创建不在同一文件，则需声明计时器。
//       2.2. 使用EXPOSE_TIMER声明计时器。它对应CREATE_TIMER创建的计时器。
//       2.3. 使用EXPOSE_TIMER_EXPORT声明计时器。它对应CREATE_TIMER_EXPORT创建的计时器。也有一次声明多个的版本：EXPOSE_TIMER_EXPORT2，EXPOSE_TIMER_EXPORT3，EXPOSE_TIMER_EXPORT4。
//     3. 计时
//       3.1. 计时开始：TIMER_START(TIMER)。如TIMER_START(NewFileCmd)。
//       3.2. 计时结束：TIMER_END(TIMER)。
//       3.3. 计时范围：TIMER_SCOPE(TIMER)。此宏定义一局部变量，并在定义处立即开始计时。在变量析构时结束计时。用于一个TIMER_START(TIMER)，但对应多处TIMER_END(TIMER)的情况。
//       3.4. 获取计时结果：TIMER_GET_TIME(TIMER)。此宏不受调试标志位影响。
//       3.5. 输出到日志：TIMER_REPORT(TIMER)。注：只有打开计算器同名调试标识位(debug mode)，计时信息才会输出到日志。如果希望日志启动即输出到日志，则需在Debug.ini中配置调试标识位。示例如下
//           /*[2016-10-27 10:11:21(102)]*/ //[FYI      ] GmepPerformanceTimer "GetAllParameters" used 2.52612ms in 1971 times 
//           /*[2016-10-27 10: 8:38(203)]*/ //[FYI      ] GmepPerformanceTimer "NewFileCmd" used 1066.23ms in 1 times 
//     4. 重置计数器
//       4.1 计时器清零，重新计数：TIMER_RESET(TIMER)。
//       4.2. 如果不清零，则按次数积累时间。
//     5.  其它：平台还提供了几个组合宏来简化代码
//       5.1 TIMER_REPORT_AND_RESET(TIMER)
//       5.2 TIMER_END_AND_REPORT_AND_RESET(TIMER)
// 
// 父子计时器
//     1. 创建父亲计数器。创建方法和创建单个计时器一样。
//     2. 创建子计时器。
//       2.1.  一次创建一个子计时器：CREATE_CHILD_TIMER(PARENT, CHILD)。
//       2.2.  一次创建多个子计时器：CREATE_CHILD_TIMER2，CREATE_CHILD_TIMER4，CREATE_CHILD_TIMER4。末尾数字表示一次创建子计时器个数。
//     3. 计时和重置
//       3.1. 使用与单个计时器同样的方法对分别对父计时器和各子计时器计时。TIMER_START(TIMER)、TIMER_END(TIMER)、TIMER_GET_TIME(TIMER)和TIMER_RESET(TIMER)。
//       3.2. 输出到日志：TIMER_REPORT(TIMER)只能作用到顶层父计时器上，它会格式化输出自己以及子计时器。示例如下：
//          /*[2016-10-27 10: 8:45(310)]*/ //[FYI      ] GmepPerformanceTimer "REGENERATION" used 240.892ms in 0 times 
//          /*[2016-10-27 10: 8:45(310)]*/ //[FYI      ]     1: "REGENERATION_EXECUTE" used 70.5017ms in 353 times 29.2669% of REGENERATION.
//          /*[2016-10-27 10: 8:45(310)]*/ //[FYI      ]     1: "REGENERATION_MARK" used 75.092ms in 1058 times 31.1724% of REGENERATION.
//          /*[2016-10-27 10: 8:45(310)]*/ //[FYI      ]         2: "MARK_GET_OBJECTS" used 26.5175ms in 1230 times 35.3134% of REGENERATION_MARK.
//          /*[2016-10-27 10: 8:45(311)]*/ //[FYI      ]         2: "MARK_GET_ObjectCalculators" used 24.1982ms in 16981 times 32.2247% of REGENERATION_MARK.
//          /*[2016-10-27 10: 8:45(311)]*/ //[FYI      ]         2: "MARK_TEST_IS_AFFECTED" used 104.892ms in 16981 times 139.685% of REGENERATION_MARK.
//          /*[2016-10-27 10: 8:45(311)]*/ //[FYI      ]     1: "REGENERATION_PROPOGATE" used 87.85ms in 1058 times 36.4686% of REGENERATION.
//          /*[2016-10-27 10: 8:45(311)]*/ //[FYI      ]     1: "REGENERATION_VALIDATE" used 7.44864ms in 48812 times 3.0921% of REGENERATION.
//     4.子计时器可同时是其它计时器的父亲。

namespace gcmp
{
    class DebugMode;
}

namespace gmep
{
    // 不要直接使用该类，应使用文件末尾处的宏定义
    class GMEP_DEV_SERVICE_EXPORT GmepPerformanceTimer
    {
    public:
        GmepPerformanceTimer(const std::wstring& timerName, const gcmp::DebugMode* pDebugMode=nullptr);
        GmepPerformanceTimer() = default;

        ~GmepPerformanceTimer(void);

        void BeginCalculating();
        void EndCalculating();

        double GetTimeInMilliseconds() const;

        void ReportResult();

        void ReportResultAsChild(int level, double parentTotal, const std::wstring& parentName);

        void Reset();

        void AddSubtimer(GmepPerformanceTimer* pTimer);
        void SetParentTimer(GmepPerformanceTimer* pParent) {m_pParentTimer=pParent;}

        bool hasBegun() const {return m_hasBegun;}

        bool IsSubTimer() const {return m_pParentTimer != nullptr;}
        bool HasDebugMode() const {return m_pDebugMode != nullptr;}
        void ClearSubTimer() {m_subTimers.clear();}
    private:
        std::wstring m_timerName;

        unsigned int m_threadId;

        // 一次计算的起止时间
        int64_t m_startTime;
        int64_t m_endTime;

        // 已经统计的时间
        int64_t m_timeElapsed;
        // 统计的次数
        int64_t m_count;

        std::vector<GmepPerformanceTimer*> m_subTimers;
        GmepPerformanceTimer* m_pParentTimer;

        // 只有在相应的debug mode设置情况下，
        const gcmp::DebugMode* m_pDebugMode;

        // 嵌套次数：一个计时器嵌套次数。
        // 一个计时器还没有结束，再次开始，为一次嵌套。
        int m_nestedCount;

        // 是否开始计时
        bool m_hasBegun;
    };

    class GMEP_DEV_SERVICE_EXPORT GmepPerformanceTimerScope
    {
    public:
        GmepPerformanceTimerScope(GmepPerformanceTimer& timer, bool reportAndRest=false)
            :m_timer(timer)
            ,m_reportAndReset(reportAndRest)
        {
            timer.BeginCalculating();
        }
        ~GmepPerformanceTimerScope()
        {
            m_timer.EndCalculating();
            if (m_reportAndReset)
            {
                m_timer.ReportResult();
                m_timer.Reset();
            }
        }
    private:
        GmepPerformanceTimer& m_timer;
        bool m_reportAndReset;
    };
}


#define EMPTY_EXPORT_TOKEN 

#define TIMER_VARIABLE(TIMER) gmep::TIMER_##TIMER
#define TIMER_SCOPE(TIMER) gmep::GmepPerformanceTimerScope TIMER_SCOPE_##TIMER(TIMER_VARIABLE(TIMER))

#define TIMER_SCOPE_REPORT_AND_RESET(TIMER) gmep::GmepPerformanceTimerScope TIMER_SCOPE_##TIMER(TIMER_VARIABLE(TIMER), true)

#define CREATE_TIMER_WITHOUT_DEBUG_MODE_IMPL(TIMER, DLL_EXPORT_TOKEN) \
    namespace gmep { DLL_EXPORT_TOKEN gmep::GmepPerformanceTimer TIMER_##TIMER(WSTRING_OF(TIMER)); } 
#define CREATE_TIMER_WITH_DEBUG_MODE_IMPL(TIMER, DLL_EXPORT_TOKEN) \
    namespace gmep { DLL_EXPORT_TOKEN gmep::GmepPerformanceTimer TIMER_##TIMER(WSTRING_OF(TIMER), &DEBUG_MODE_VARIABLE(TIMER)); } 

#define CREATE_TIMER_IMPL(TIMER, OWNER, REVIEW_TIME, USAGE, DLL_EXPORT_TOKEN) \
    CREATE_DEBUG_MODE(TIMER, WSTRING_OF(TIMER), gcmp::DebugModeGroup::DMGT_PERFORMANCE_TIMER, OWNER, REVIEW_TIME, USAGE); \
    CREATE_TIMER_WITH_DEBUG_MODE_IMPL(TIMER, DLL_EXPORT_TOKEN)

#define CREATE_TIMER_IMPL2(TIMER1, TIMER2, OWNER, REVIEW_TIME, USAGE, DLL_EXPORT_TOKEN) \
    CREATE_TIMER_IMPL(TIMER1, OWNER, DLL_EXPORT_TOKEN, REVIEW_TIME); \
    CREATE_TIMER_IMPL(TIMER2, OWNER, DLL_EXPORT_TOKEN, REVIEW_TIME)

#define CREATE_TIMER_IMPL3(TIMER1, TIMER2, TIMER3, OWNER, REVIEW_TIME, USAGE, DLL_EXPORT_TOKEN) \
    CREATE_TIMER_IMPL2(TIMER1, TIMER2, OWNER, REVIEW_TIME, USAGE, DLL_EXPORT_TOKEN); \
    CREATE_TIMER_IMPL(TIMER3, OWNER, REVIEW_TIME, USAGE, DLL_EXPORT_TOKEN)

#define CREATE_TIMER_IMPL4(TIMER1, TIMER2, TIMER3, TIMER4, OWNER, REVIEW_TIME, USAGE, DLL_EXPORT_TOKEN) \
    CREATE_TIMER_IMPL3(TIMER1, TIMER2, TIMER3, OWNER, REVIEW_TIME, USAGE, DLL_EXPORT_TOKEN); \
    CREATE_TIMER_IMPL(TIMER4, OWNER, REVIEW_TIME, USAGE, DLL_EXPORT_TOKEN)
//////////////////////////////////////////////////////////////////////////

// Create timer
#define CREATE_TIMER(TIMER, OWNER, REVIEW_TIME, USAGE) \
    CREATE_TIMER_IMPL(TIMER, OWNER, REVIEW_TIME, USAGE, EMPTY_EXPORT_TOKEN)

#define CREATE_TIMER2(TIMER1, TIMER2, OWNER, REVIEW_TIME, USAGE) \
    CREATE_TIMER_IMPL2(TIMER1, TIMER2, OWNER, REVIEW_TIME, USAGE, EMPTY_EXPORT_TOKEN)

#define CREATE_TIMER3(TIMER1, TIMER2, TIMER3, OWNER, REVIEW_TIME, USAGE) \
    CREATE_TIMER_IMPL3(TIMER1, TIMER2, TIMER3, OWNER, REVIEW_TIME, USAGE, EMPTY_EXPORT_TOKEN)

#define CREATE_TIMER4(TIMER1, TIMER2, TIMER3, TIMER4, OWNER, REVIEW_TIME, USAGE) \
    CREATE_TIMER_IMPL4(TIMER1, TIMER2, TIMER3, TIMER4, OWNER, REVIEW_TIME, USAGE, EMPTY_EXPORT_TOKEN)
//////////////////////////////////////////////////////////////////////////

// create timer export
#define CREATE_TIMER_EXPORT(TIMER, OWNER, REVIEW_TIME, USAGE) \
    CREATE_TIMER_IMPL(TIMER, OWNER, REVIEW_TIME, USAGE, DLL_EXPORT)

#define CREATE_TIMER_EXPORT2(TIMER1, TIMER2, OWNER, REVIEW_TIME, USAGE) \
    CREATE_TIMER_IMPL2(TIMER1, TIMER2, OWNER, REVIEW_TIME, USAGE, DLL_EXPORT)

#define CREATE_TIMER_EXPORT3(TIMER1, TIMER2, TIMER3, OWNER, REVIEW_TIME, USAGE) \
    CREATE_TIMER_IMPL3(TIMER1, TIMER2, TIMER3, OWNER, REVIEW_TIME, USAGE, DLL_EXPORT)

#define CREATE_TIMER_EXPORT4(TIMER1, TIMER2, TIMER3, TIMER4, OWNER) \
    CREATE_TIMER_IMPL4(TIMER1, TIMER2, TIMER3, TIMER4, OWNER, REVIEW_TIME, USAGE, DLL_EXPORT)
//////////////////////////////////////////////////////////////////////////

#define CREATE_TIMER_RELATIONSHIP(PARENT, CHILD) \
    namespace {auto PARENT##_##CHILD##_RELETIONSHIP = [](){TIMER_VARIABLE(PARENT).AddSubtimer(&TIMER_VARIABLE(CHILD)); TIMER_VARIABLE(CHILD).SetParentTimer(&TIMER_VARIABLE(PARENT));}; } \
    ONE_TIME_INITIALIZER(PARENT##_##CHILD##_RELETIONSHIP)

// create child timer
#define CREATE_CHILD_TIMER(PARENT, CHILD) \
    CREATE_TIMER_WITHOUT_DEBUG_MODE_IMPL(CHILD, EMPTY_EXPORT_TOKEN); \
    CREATE_TIMER_RELATIONSHIP(PARENT, CHILD)

#define CREATE_CHILD_TIMER2(PARENT, CHILD1, CHILD2) \
    CREATE_CHILD_TIMER(PARENT, CHILD1); \
    CREATE_CHILD_TIMER(PARENT, CHILD2); 

#define CREATE_CHILD_TIMER3(PARENT, CHILD1, CHILD2, CHILD3) \
    CREATE_CHILD_TIMER2(PARENT, CHILD1, CHILD2); \
    CREATE_CHILD_TIMER(PARENT, CHILD3); 

#define CREATE_CHILD_TIMER4(PARENT, CHILD1, CHILD2, CHILD3, CHILD4) \
    CREATE_CHILD_TIMER3(PARENT, CHILD1, CHILD2, CHILD3); \
    CREATE_CHILD_TIMER(PARENT, CHILD4); 

// create child timer export
#define CREATE_CHILD_TIMER_EXPORT(PARENT, CHILD) \
    CREATE_TIMER_WITHOUT_DEBUG_MODE_IMPL(CHILD, DLL_EXPORT); \
    CREATE_TIMER_RELATIONSHIP(PARENT, CHILD)

#define CREATE_CHILD_TIMER_EXPORT2(PARENT, CHILD1, CHILD2) \
    CREATE_CHILD_TIMER_EXPORT(PARENT, CHILD1); \
    CREATE_CHILD_TIMER_EXPORT(PARENT, CHILD2)

#define CREATE_CHILD_TIMER_EXPORT3(PARENT, CHILD1, CHILD2, CHILD3) \
    CREATE_CHILD_TIMER_EXPORT2(PARENT, CHILD1, CHILD2); \
    CREATE_CHILD_TIMER_EXPORT(PARENT, CHILD3)

#define CREATE_CHILD_TIMER_EXPORT4(PARENT, CHILD1, CHILD2, CHILD3, CHILD4) \
    CREATE_CHILD_TIMER_EXPORT3(PARENT, CHILD1, CHILD2, CHILD3); \
    CREATE_CHILD_TIMER_EXPORT(PARENT, CHILD4)

// expose timer
#define EXPOSE_TIMER(TIMER) namespace gmep { extern gmep::GmepPerformanceTimer TIMER_##TIMER; }

#define EXPOSE_TIMER2(TIMER1, TIMER2) \
    EXPOSE_TIMER(TIMER1); \
    EXPOSE_TIMER(TIMER2)

#define EXPOSE_TIMER3(TIMER1, TIMER2, TIMER3) \
    EXPOSE_TIMER2(TIMER1, TIMER2); \
    EXPOSE_TIMER(TIMER3)

#define EXPOSE_TIMER4(TIMER1, TIMER2, TIMER3, TIMER4) \
    EXPOSE_TIMER3(TIMER1, TIMER2, TIMER3); \
    EXPOSE_TIMER(TIMER4)

// expose timer export
#define EXPOSE_TIMER_EXPORT(TIMER, DLL_EXPORT_TOKEN) namespace gmep { extern DLL_EXPORT_TOKEN gmep::GmepPerformanceTimer TIMER_##TIMER; }

#define EXPOSE_TIMER_EXPORT2(TIMER1, TIMER2, DLL_EXPORT_TOKEN) \
    EXPOSE_TIMER_EXPORT(TIMER1, DLL_EXPORT_TOKEN); \
    EXPOSE_TIMER_EXPORT(TIMER2, DLL_EXPORT_TOKEN)

#define EXPOSE_TIMER_EXPORT3(TIMER1, TIMER2, TIMER3, DLL_EXPORT_TOKEN) \
    EXPOSE_TIMER_EXPORT2(TIMER1, TIMER2, DLL_EXPORT_TOKEN); \
    EXPOSE_TIMER_EXPORT(TIMER3, DLL_EXPORT_TOKEN)

#define EXPOSE_TIMER_EXPORT4(TIMER1, TIMER2, TIMER3, TIMER4, DLL_EXPORT_TOKEN) \
    EXPOSE_TIMER_EXPORT3(TIMER1, TIMER2, TIMER3, DLL_EXPORT_TOKEN); \
    EXPOSE_TIMER_EXPORT(TIMER4, DLL_EXPORT_TOKEN)

#define TIMER_START(TIMER) TIMER_VARIABLE(TIMER).BeginCalculating()
#define TIMER_END(TIMER) TIMER_VARIABLE(TIMER).EndCalculating()
#define TIMER_REPORT(TIMER) TIMER_VARIABLE(TIMER).ReportResult()
#define TIMER_RESET(TIMER) TIMER_VARIABLE(TIMER).Reset()
#define TIMER_GET_TIME(TIMER) TIMER_VARIABLE(TIMER).GetTimeInMilliseconds()
#define TIMER_REPORT_AND_RESET(TIMER) TIMER_REPORT(TIMER);TIMER_RESET(TIMER)
#define TIMER_END_AND_REPORT_AND_RESET(TIMER) TIMER_END(TIMER); TIMER_REPORT_AND_RESET(TIMER)

#pragma warning( pop )
